//!
/************************************************************************************************************
 * @file RBD3D9FXTechnique.cpp
 ************************************************************************************************************/

/************************************************************************************************************
 * Precompiled Header
 ************************************************************************************************************/
#include "RBD3D9RendererPCH.h"
 
/************************************************************************************************************
 * Includes
 ************************************************************************************************************/

#include <RBMain/RBTechniqueParameter.h>

#include "RBD3D9FXTechnique.h"
#include "RBD3D9Renderer.h"
#include "RBD3D9Tools.h"
#include "RBD3D9Texture.h"



/************************************************************************************************************
 * Defines
 ************************************************************************************************************/

/************************************************************************************************************
 * Implementation
 ************************************************************************************************************/

/************************************************************************************************************
 * Constructor
 ************************************************************************************************************/
RBD3D9FXTechnique::RBD3D9FXTechnique(RBRenderer* pRenderer, ID3DXEffect* pEffect, D3DXHANDLE hTechnique)
: Super(pRenderer)
, m_hTechnique(hTechnique)
, m_pEffect(pEffect)
{
}

/************************************************************************************************************
 * Destructor
 ************************************************************************************************************/
RBD3D9FXTechnique::~RBD3D9FXTechnique()
{
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
bool RBD3D9FXTechnique::BeginPasses(const Renderable&)
{
    m_pEffect->SetTechnique(m_hTechnique);

    UINT uiPassCount;
    uint32 uiStatesNotSaved = 0; //D3DXFX_DONOTSAVESTATE | D3DXFX_DONOTSAVESAMPLERSTATE | D3DXFX_DONOTSAVESHADERSTATE;
    HRESULT hr = m_pEffect->Begin(&uiPassCount, uiStatesNotSaved);
    m_uiPassCount = (uint32) uiPassCount;
    return SUCCEEDED(hr);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D9FXTechnique::EndPasses(const Renderable&)
{
    m_pEffect->End();
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D9FXTechnique::BeginPass(const Renderable&, uint32 uiPass)
{
    m_pEffect->BeginPass(uiPass);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D9FXTechnique::EndPass(const Renderable& rb, uint32 uiPass)
{
    Super::EndPass(rb, uiPass);
    m_pEffect->EndPass();
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D9FXTechnique::SetupShaders(const Renderable& rbRenderable, uint32 uiPass)
{
    Super::SetupShaders(rbRenderable, uiPass);
    // Apply all parameters to shader
    m_pEffect->CommitChanges();
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBD3D9FXTechnique::SetMatrix(const float4x4* mat, const RBTechniqueParameter* pParam, uint32 uiCount)
{
    if (uiCount < 2)
    {
        static D3DXMATRIX d3dMatrix;
        RBD3D9Tools::ComputeD3DFromRB(d3dMatrix, *mat);
        m_pEffect->SetMatrix((D3DXHANDLE)pParam->GetHandle(), &d3dMatrix);
    }
    else
    {
        D3DXMATRIX* d3dMatrix = new D3DXMATRIX[uiCount];
        for (uint32 ui=0; ui<uiCount; ++ui)
        {
            RBD3D9Tools::ComputeD3DFromRB(d3dMatrix[ui], mat[ui]);
        }
        m_pEffect->SetMatrixArray((D3DXHANDLE)pParam->GetHandle(), d3dMatrix, uiCount);
        delete[] d3dMatrix;

    }
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBD3D9FXTechnique::SetVector(const float4* vec, const RBTechniqueParameter* pParam, uint32 uiCount)
{
    if (uiCount < 2)
    {
        m_pEffect->SetVector((D3DXHANDLE)pParam->GetHandle(), (const D3DXVECTOR4*)&vec);
    }
    else
    {
        m_pEffect->SetVectorArray((D3DXHANDLE)pParam->GetHandle(), (const D3DXVECTOR4*)&vec, uiCount);
    }
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBD3D9FXTechnique::SetTexture(const RBTexture* pTexture, const RBTechniqueParameter* pParam)
{
    m_pEffect->SetTexture((D3DXHANDLE)pParam->GetHandle(), static_cast<const RBD3D9Texture*>(pTexture)->GetTexture());
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
void RBD3D9FXTechnique::SetValue(const void* pData, uint32 uiSize, const RBTechniqueParameter* pParam)
{
    m_pEffect->SetValue((D3DXHANDLE)pParam->GetHandle(), pData, uiSize);
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBTechniqueParameter* CreateParameter(const D3DXPARAMETER_DESC& descParam, ID3DXEffect* pEffect, D3DXHANDLE hParam)
{
    RBTechniqueParameter* pParameter = NULL;
    if (descParam.Type == D3DXPT_TEXTURE ||
        descParam.Type == D3DXPT_TEXTURE1D || 
        descParam.Type == D3DXPT_TEXTURE2D ||
        descParam.Type == D3DXPT_TEXTURE3D ||
        descParam.Type == D3DXPT_TEXTURECUBE)
    {
        const char* pcRBM = 0;
        D3DXHANDLE hRBM = pEffect->GetAnnotationByName(hParam, "RBM");
        if (hRBM)
        {
            // We found an annotation called RBM: we will deduce the associated texture 
            pEffect->GetString(hRBM, &pcRBM);
        }

        if (!pParameter)
        {
            pParameter = new RBTechniqueParameter(pcRBM, (void*)hParam, RBRenderer::GetSingleton());
        }
    }
    else if (descParam.Type == D3DXPT_SAMPLER ||
        descParam.Type == D3DXPT_SAMPLER1D ||
        descParam.Type == D3DXPT_SAMPLER2D ||
        descParam.Type == D3DXPT_SAMPLER3D ||
        descParam.Type == D3DXPT_SAMPLERCUBE)
    {
    }
    else
    {
        RBTechniqueParameter::Type eType = RBTechniqueParameter::TPT_UNKNOWN;

        // Bools and ints are supposed to be packed as vectors
        switch(descParam.Type)
        {
        case D3DXPT_BOOL:
            eType = (descParam.Class == D3DXPC_VECTOR) ? (RBTechniqueParameter::Type) (RBTechniqueParameter::TPT_BOOL + descParam.Columns - 1)
                                                       : RBTechniqueParameter::TPT_BOOL;
        	break;
        case D3DXPT_INT:
            eType = (descParam.Class == D3DXPC_VECTOR) ? (RBTechniqueParameter::Type) (RBTechniqueParameter::TPT_INT + descParam.Columns - 1)
                                                       : RBTechniqueParameter::TPT_INT;
        	break;
        case D3DXPT_FLOAT:
            if (descParam.Class == D3DXPC_VECTOR)
            {
                eType = (RBTechniqueParameter::Type) (RBTechniqueParameter::TPT_FLOAT + descParam.Columns - 1 );
            }
            else if (descParam.Class == D3DXPC_MATRIX_ROWS || descParam.Class == D3DXPC_MATRIX_COLUMNS)
            {
                eType = (descParam.Rows == 3 && descParam.Columns == 3) ? RBTechniqueParameter::TPT_FLOAT33 :
                        (descParam.Rows == 4 && descParam.Columns == 4) ? RBTechniqueParameter::TPT_FLOAT44 :
                        (descParam.Rows == 3 && descParam.Columns == 4) ? RBTechniqueParameter::TPT_FLOAT43 :
                        (descParam.Rows == 4 && descParam.Columns == 3) ? RBTechniqueParameter::TPT_FLOAT43
                                                                        : RBTechniqueParameter::TPT_UNKNOWN ;
            }
            else
            {
                eType = RBTechniqueParameter::TPT_FLOAT;
            }
            break;
        default:
            break;
        }

        RBTechniqueParameter::AdditionalParameters kParams;

        // If we have a float2, try to find out if we have an annotation called RBM
        if (descParam.Type == D3DXPT_FLOAT && 
            descParam.Class == D3DXPC_VECTOR &&
            descParam.Columns == 2)
        {
            D3DXHANDLE hRBM = pEffect->GetAnnotationByName(hParam, "RBM");
            if (hRBM)
            {
                if (hRBM)
                {
                    const char* pcRBM = "";
                    // We found an annotation called RBM: we will deduce the associated texture 
                    pEffect->GetString(hRBM, &pcRBM);
                    kParams.SamplerType = RBMaterial::GetSamplerType(pcRBM);
                }
            }
        }

        if (eType != RBTechniqueParameter::TPT_UNKNOWN)
        {
            pParameter = new RBTechniqueParameter(descParam.Name, eType, descParam.Semantic, (void*)hParam, RBRenderer::GetSingleton(), descParam.Elements, kParams);
        }

    }

    return pParameter;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
bool RBD3D9FXTechnique::Load(bool bReload)
{
    // We Use the technique's name
    D3DXTECHNIQUE_DESC d3dTechniqueDesc;
    if (SUCCEEDED(m_pEffect->GetTechniqueDesc(m_hTechnique, &d3dTechniqueDesc)))
    {
        SetName(d3dTechniqueDesc.Name);
    }

    // We look for all the parameters used by this technique
    D3DXEFFECT_DESC d3dEffectDesc;
    if (SUCCEEDED(m_pEffect->GetDesc(&d3dEffectDesc)))
    {
        //We iterate through all the parameters
        for (unsigned int uiParameter = 0; uiParameter < d3dEffectDesc.Parameters; ++uiParameter)
        {
            D3DXHANDLE hParam = m_pEffect->GetParameter(NULL, uiParameter);
            D3DXPARAMETER_DESC descParam;
            if (SUCCEEDED(m_pEffect->GetParameterDesc(hParam, &descParam)))
            {
                // We have a parameter currently used: Add it to the technique

                RBTechniqueParameter* pParameter = NULL;
                
                if (bReload)
                {
                    pParameter = GetParameterByName(descParam.Name);
                }

                bool bNewParameter = false;

                if (!pParameter)
                {
                    pParameter = CreateParameter(descParam, m_pEffect, hParam);
                    bNewParameter = true;
                }

                if (pParameter)
                {
                    if (bNewParameter)
                    {
                        AddParameter(descParam.Name, pParameter);
                    }
                    pParameter->SetHandle((void*)hParam);
                    pParameter->IsUsed(m_pEffect->IsParameterUsed(hParam, m_hTechnique) == TRUE);
                }

            }
        }
    }

    return m_pEffect != 0;
}

/************************************************************************************************************/
